package fxbattle.client.coms;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import fxbattle.client.model.BattleHex;
import fxbattle.client.model.BattleMap;
import fxbattle.client.model.BoundaryHex;
import fxbattle.client.model.Direction;
import fxbattle.protos.ClientProtos;
import fxbattle.protos.ServerProtos;
import fxbattle.protos.ClientProtos.*;
import fxbattle.protos.ServerProtos.*;
import fxbattle.protos.ServerProtos.NodeType.KindOfNode;
import fxbattle.protos.SharedProtos.*;

public class DummyServer {

	public static final int DUMMY_SOCKET = 43212;

	private final ServerSocket fMySocket;
	private Socket fClientSocket = null;
	
	public DummyServer() {
		this(DUMMY_SOCKET);
	}
	
	public DummyServer(int iPortToListenOn) {
		print("New Server made to listen to port: " + iPortToListenOn);
		ServerSocket lSocket = null;
		 try {
			 lSocket = new ServerSocket(iPortToListenOn);
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
		fMySocket = lSocket;
	}
	
	public void waitForClient(){
		try {
			print("Server Waiting for Connection.");
			fClientSocket = fMySocket.accept();
			print("Server Got Connection!");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void waitForRequestToPlay() {
		print("Server waiting for a request to play.");
		while(true){
			ClientProtos.RequestToPlay lRequest = null;
			try {
				print("Parsing from Client...");
				lRequest = ClientProtos.RequestToPlay.parseDelimitedFrom(fClientSocket.getInputStream());
				print("Done Parsing from Client");
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			if(lRequest != null && lRequest.isInitialized()){
				print("Found an initalized request for name of " + lRequest.getName());
				ServerProtos.Player lPlayer = ServerProtos.Player.newBuilder().setName(lRequest.getName()).setColor(RgbColor.newBuilder().setRed(1).setBlue(1).setGreen(1).build()).build();
				try {
					ServerProtos.RespondToPlayRequest.newBuilder().setYou(lPlayer).build().writeDelimitedTo(fClientSocket.getOutputStream());
					print("Sent a player.");
				} catch (IOException e) {
					print("Couldn't send back a player.");
					e.printStackTrace();
				}
				break;
			} else {
				print("Didn't get a request yet.");
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	private final BattleMap fMap = new BattleMap();
	{ // Initialize the map.
		for(int i = 0; i < 10; i++){
			for(int y = 0; y < 10; y++){
                BattleHex lHex = new BattleHex(fxbattle.client.model.Location.getLocation(i, y));
                lHex.setElevation(y);
				fMap.setHex(lHex);
			}
		}
		fMap.assignNeighbors();
	}
	
	public void sendFirstUpdate(){
		System.out.println("Sending First Update");
		List<NodeChange> lNodes = new ArrayList<NodeChange>();
		List<PathAvailabilityChanged> lPaths = new ArrayList<PathAvailabilityChanged>();
		for(int i = fMap.getMinX(); i < fMap.getMaxX(); i++){
			for(int y = fMap.getMinY(); y < fMap.getMaxY(); y++){
				if( ! (fMap.getHex(i, y) instanceof BoundaryHex) ){
					// This is hacky and wrong, but that's okay-- this server is dumb.
					for(Direction lDir: Direction.values()){
						if(fMap.getHex(i, y).canActivateFlow(lDir)){
							ProtoLocation origin = ProtoLocation.newBuilder().setLocX(i).setLocY(y).build();
							ProtoLocation dest = ProtoLocation.newBuilder().setLocX(lDir.goFrom(fxbattle.client.model.Location.getLocation(i, y)).getX()).setLocY(fxbattle.client.model.Location.getLocation(i, y).getY()).build();
							lPaths.add(PathAvailabilityChanged.newBuilder().setIsAvailable(true).setToChange(Path.newBuilder().setOrigin(origin).setDestination(dest)).build());
						}
					}
					NodeType lType = NodeType.newBuilder().setKind( i % 2 == 0 ? KindOfNode.BASE : KindOfNode.NORMAL).build();
					ProtoLocation lLoc = ProtoLocation.newBuilder().setLocX(i).setLocY(y).build();
					lNodes.add(NodeChange.newBuilder().setLoc(lLoc).setElevation(fMap.getHex(i,y).getElevation()).setType(lType).build());
				}
			}
		}
		Player lPlayer = ServerProtos.Player.newBuilder().setName("Joe").setColor(RgbColor.newBuilder().setRed(1).setBlue(1).setGreen(1).build()).build();
		ProtoLocation loc = ProtoLocation.newBuilder().setLocX(0).setLocY(0).build();
		ServerProtos.StateUpdate lFirstUpdate = 
			ServerProtos.StateUpdate.newBuilder().addAllNodeChange(lNodes).addAllArmyChange(Arrays.asList(
					ServerProtos.NodeOccupationChanged.newBuilder().setLoc(loc).setOwner(lPlayer).setSize(30.0).build())
					).addAllPathChange(lPaths).build();
		try {
			lFirstUpdate.writeDelimitedTo(fClientSocket.getOutputStream());
		} catch (IOException e) {
			System.out.println("Couldn't write to socket.");
			e.printStackTrace();
		}
	}

	
	public void playGame() {
		print("Starting the Game");
		boolean ioBroken = false;
		while(fClientSocket != null && fClientSocket.isConnected() && ! ioBroken){
			print("Playing...");
			try {
				SetPath lPath = SetPath.parseDelimitedFrom(fClientSocket.getInputStream());
				ServerProtos.StateUpdate.newBuilder().addFlowChange(convert(lPath)).build().writeDelimitedTo(fClientSocket.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
				ioBroken = true;
				try {
					System.out.println("Closing Connection.");
					fClientSocket.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
		print("Game Over I Win.");
	}
	
	private static FlowPathChange convert(SetPath iPath){
		System.out.println("Converting: " + iPath); 
		return FlowPathChange.newBuilder().setIsFlowing(iPath.getIsActive()).setPath(iPath.getToChange()).build();
	}
	
//	private static ProtoLocation convert(ProtoLocation iLoc){
//		return ProtoLocation.newBuilder().setLocX(iLoc.getLocX()).setLocY(iLoc.getLocY()).build();
//	}
	
	public static void print(Object iObj){
		System.out.println(Thread.currentThread().getId() + ": " + iObj.toString());
	}
	
	public static void spinOffServerThread(){
		new Thread("Pretend Server Thread"){
			@Override
			public void run() {
				DummyServer lServe = new DummyServer();
				lServe.waitForClient();
				lServe.waitForRequestToPlay();
				lServe.sendFirstUpdate();
				lServe.playGame();
				print("Server Thread Done");
			}
		}.start();
	}
	
	public static void main(String[] args) {
		spinOffServerThread();
		
//		ServerProtos.Player.newBuilder().set
		
	}

}
